package Screens 
{
	import DeviceData.CameraEvent;
	import DeviceData.GPSErrorEvent;
	import feathers.controls.ButtonGroup;
	import feathers.controls.Label;
	import feathers.controls.Screen;
	import DeviceData.CameraData;
	import DeviceData.GeoLocationData;
	import feathers.data.ListCollection;
	import flash.display.Loader;
	import flash.events.TimerEvent;
	import flash.system.Capabilities;
	import flash.utils.Timer;
	import starling.display.Sprite;
	import flash.events.ErrorEvent;
	import flash.events.FullScreenEvent;
	import starling.events.Event;
	import flash.events.GeolocationEvent;
	import flash.events.MouseEvent;
	
	import Screens.NavigationEvent;
	import Screens.DataEvent;
	/**
	 * @author Simon Claeye
	 */
	public class CaptureScreen extends Screen
	{
		private var camData:CameraData;
		private var geoData:GeoLocationData;
		private var geoEnabled:Boolean;
		private var noDataContainer:Sprite;
		private var noDataContainerAdded:Boolean = false;
		
		private var image:Loader;
		private var imageNativePath:String;
		private var geoEvent:GeolocationEvent;
		private var error:Boolean = false;
		
		private var timer:Timer;
		
		public function CaptureScreen() 
		{
			this.addEventListener( Event.ADDED_TO_STAGE, addedToStageHandler );
		}
		
		private function addedToStageHandler(event:Event):void
		{
			// handles the back and menu hardware keys on android
			this.backButtonHandler = backHandler;
			this.menuButtonHandler = menuHandler;
			
			camData = new CameraData();
			geoData = new GeoLocationData();
			
			camData.addEventListener(Event.COMPLETE, onImageTaken);
			camData.addEventListener(Event.CANCEL, onCanceled);
			camData.addEventListener(CameraEvent.UNSUPPORTED, onCamUnsupported);

			geoData.addEventListener(DataEvent.LOCATION_CHANGE, onGeoUpdate);
			geoData.addEventListener(GPSErrorEvent.MUTED, onGPSMuted);
			geoData.addEventListener(GPSErrorEvent.UNSUPPORTED, onGPSUnsupported);
			geoData.initialize();
			geoEnabled = true;	//assume enabled until error
			
			camData.launchCamera();
		}
		
		/**
		 * A function to dispatch an event when the back hardware key
		 * is pressed on android.
		 */
		private function backHandler():void
		{
			dispatchEvent(new NavigationEvent(NavigationEvent.RETURN));
		}
		
		/**
		* A function to dispatch an event when the menu hardware key
		* is pressed.
		*/
		private function menuHandler():void
		{
			dispatchEvent(new NavigationEvent(NavigationEvent.MAIN_MENU));
		}
		
		/**
		 * Function to handle the event in which the camera is not supported.
		 * @param	event
		 */
		private function onCamUnsupported(event:CameraEvent):void
		{
						//flag that an error has occurred
			error = true;
			
			noDataContainer = new Sprite();
			noDataContainer.x = 10;
			
			var label:Label = new Label();
			label.text = "This device does not support\n" +
			" necessary camera functionality.";
			label.y = 1 * (this.stage.stageHeight / 20);
			
			var group:ButtonGroup = new ButtonGroup();
			group.dataProvider = new ListCollection(
			[
				{ label: "Continue", triggered: cancelHandler },
			]);
			group.direction = ButtonGroup.DIRECTION_HORIZONTAL;
			group.gap = 10;
			group.y = 15 * (this.stage.stageHeight / 20);
			
			noDataContainer.addChild(label);
			noDataContainer.addChild(group);
			noDataContainerAdded = true;
			addChild(noDataContainer);
		}
		
		/**
		 * Function to handle the event in which the gps is muted (present but turned off by the user).
		 * @param	event
		 */
		private function onGPSMuted(event:GPSErrorEvent):void
		{
			//flag that an error has occurred
			error = true;
			
			noDataContainer = new Sprite();
			noDataContainer.x = 10;
			
			var iOS:Boolean = Capabilities.manufacturer.indexOf("iOS") != -1;
			
			var label:Label = new Label();
			if (iOS)
			{
				label.text = "GPS not enabled.\n\n" +
					"GPS should be enabled if you\nwant position information\nto be collected.\n\n"+	
					"To resolve this issue:\n\n" +
					"This option is available under\n" +
					"Settings >\nGeneral >\nLocation Services";
			} else
			{
				label.text = "GPS not enabled.\n\n" +
					"GPS should be enabled if you\nwant position information\nto be collected.\n\n"+	
					"To resolve this issue:\n\n" +
					"On most Android phones,\nthis option will be under\n" +
					"Settings >\nLocation and Security Settings >\nMy Location sources.";
			}
			label.y = 1 * (this.stage.stageHeight / 20);
			
			var group:ButtonGroup = new ButtonGroup();
			group.dataProvider = new ListCollection(
			[
				{ label: "Cancel", triggered: cancelHandler },
				{ label: "Continue", triggered: continueWithoutDataHandler },
			]);
			group.direction = ButtonGroup.DIRECTION_HORIZONTAL;
			group.gap = 10;
			group.y = 15 * (this.stage.stageHeight / 20);
			
			noDataContainer.addChild(label);
			noDataContainer.addChild(group);
			noDataContainerAdded = true;
			addChild(noDataContainer);
		}
		
		private function cancelHandler(event:Event):void
		{
			dispatchEvent(new NavigationEvent(NavigationEvent.RETURN));
		}
		
		private function onGPSUnsupported(event:GPSErrorEvent):void
		{
			error = true;
			continueWithoutDataHandler(new Event(Event.TRIGGERED));
		}
		
		public function getImage():Loader
		{
			return image;
		}
		
		public function getImageNativePath():String
		{
			return imageNativePath;
		}
		
		public function getGeoData():GeolocationEvent
		{
			return geoEvent;
		}
		
		public function hasGEOData():Boolean
		{
			return geoEvent != null;
		}
		
		private function onCanceled(event:Object):void
		{
			//cleanup
			this.geoData.stop();
			this.geoData.removeEventListener(DataEvent.LOCATION_CHANGE, onGeoUpdate);
			this.geoData.addEventListener(GPSErrorEvent.MUTED, onGPSMuted);
			this.geoData.addEventListener(GPSErrorEvent.UNSUPPORTED, onGPSUnsupported);
			this.geoData = null;
			dispatchEvent(new NavigationEvent(NavigationEvent.RETURN));
		}
		
		private function onGPSError(event:ErrorEvent):void
		{
			continuePrompt();
		}
		
		private function onImageTaken(event:Object):void
		{
			camData.removeEventListener(Event.COMPLETE, onImageTaken);
			onDataGathered();
		}
		
		private function onGeoUpdate(event:DataEvent):void
		{
			this.geoData.removeEventListener(DataEvent.LOCATION_CHANGE, onGeoUpdate);
			this.geoData.addEventListener(GPSErrorEvent.MUTED, onGPSMuted);
			this.geoData.addEventListener(GPSErrorEvent.UNSUPPORTED, onGPSUnsupported);
			onDataGathered();
		}
		
		/**
		 * Function to prompt user to wait for a GPS signal or to continue.
		 */
		private function continuePrompt():void
		{
			if (!error)
			{
				noDataContainer = new Sprite();
				noDataContainer.x = 10;
			
				var label:Label = new Label();
				label.text = "Could not receive a GPS signal.\n" +
					"Would you like to for a signal\n" +
					"to be established?";
				label.y = 1 * (this.stage.stageHeight / 20);
				
				var group:ButtonGroup = new ButtonGroup();
				group.dataProvider = new ListCollection(
				[
					{ label: "Wait", triggered: waitForDataHandler },
					{ label: "Continue", triggered: continueWithoutDataHandler },
				]);
				group.direction = ButtonGroup.DIRECTION_HORIZONTAL;
				group.gap = 10;
				group.y = 5 * (this.stage.stageHeight / 20);
				
				noDataContainer.addChild(label);
				noDataContainer.addChild(group);
				noDataContainerAdded = true;
				
				addChild(noDataContainer);
			}
		}
		
		/**
		 * Function to continue without waiting for GPS data.
		 * @param	event
		 */
		private function continueWithoutDataHandler(event:Event):void
		{
			if (noDataContainerAdded)
			{
				noDataContainerAdded = false;
				removeChild(noDataContainer);
			}
			
			image = camData.getImage();
			imageNativePath = camData.getNativePath();
			
			//cleanup
			geoData.stop();
			geoData.removeEventListener(DataEvent.LOCATION_CHANGE, onGeoUpdate);
			
			geoData = null;
			
			dispatchEvent(new NavigationEvent(NavigationEvent.EDIT));
		}
		
		private function waitForDataHandler(event:Event):void
		{
			this.timer = new Timer(1000, 30);	//run the time for 30 seconds
			this.timer.addEventListener(TimerEvent.TIMER_COMPLETE, onOutOfTime);
			this.timer.start();
		}
		
		private function onOutOfTime(event:TimerEvent):void
		{
			this.timer = null;
			removeChild(noDataContainer);
			continuePrompt();
		}
		
		/**
		 * Callback function invoked when data has been gathered from either the camera or the gps.
		 */
		private function onDataGathered():void
		{
			if (geoEnabled)
			{
				if (geoData.hasData() && camData.hasCapturedImage())
				{
					if (timer != null)
					{
						timer.stop();
						timer = null;
						removeChild(noDataContainer);
					}
					
					//retrieve the most recent gps data data associated with the image
					geoEvent = geoData.getData();
					image = camData.getImage();
					imageNativePath = camData.getNativePath();
					
					//cleanup
					geoData.stop();
					geoData.removeEventListener(DataEvent.LOCATION_CHANGE, onGeoUpdate);
					geoData.addEventListener(GPSErrorEvent.MUTED, onGPSMuted);
					geoData.addEventListener(GPSErrorEvent.UNSUPPORTED, onGPSUnsupported);
					geoData = null;
					camData = null;
					dispatchEvent(new NavigationEvent(NavigationEvent.EDIT));
				}
				else if (!geoData.hasData())
					continuePrompt();
			}
			else
			{
				image = camData.getImage();
				imageNativePath = camData.getNativePath();
				geoEvent = null;
				
				//cleanup
				camData = null;
				
				dispatchEvent(new NavigationEvent(NavigationEvent.EDIT));
			}
		}	
	}
}